package sg.nus.iss.vmcs.controllers;

import sg.nus.iss.vmcs.VMCSException;
import sg.nus.iss.vmcs.gui.CustomerPanel;
import sg.nus.iss.vmcs.gui.GUIChangeManager;
import sg.nus.iss.vmcs.gui.GUIChangeManagerEvent;
import sg.nus.iss.vmcs.gui.MessageDialog;
import sg.nus.iss.vmcs.utils.StoreType;

/**
 * Transaction controller managing customer purchase transactions
 * @author Philip
 */
public class TransactionController {
	
	/** Used to indicate if change has been given */
	private boolean changeGiven;
	
	/** Used to indicate if drinks have successfully been dispensed during the transaction */
	private boolean drinkDispensed;
	
	/** Price of the selected drink */
	private int price;
	
	/** Idenfitifer of the selected drink */
	private int selection;
	
	/** Instance of the Customer Panel managed by this controller */
	private CustomerPanel cPanel;
	
	/** Instance of the MainController for this Controller */
	private MainController mCtrl;

	/** Coin Receiver managed under this controller */
	private CoinReceiver coinReceiver;
	
	/** Dispense Controller managed under this controller */
	private DispenseController dispenseCtrl;
	
	/** Change Giver managed under this controller */
	private ChangeGiver changeGiver;
	
	/**
	 * Creates a new instance of the Transaction Controller
	 * @param mCtrl
	 */
	public TransactionController(MainController mCtrl) {
		//Initialize variables
		resetTransactionVariables();
		
		//Setup Controllers
		this.mCtrl 		= mCtrl;
		coinReceiver	= new CoinReceiver(this);
		dispenseCtrl	= new DispenseController(this);
		changeGiver		= new ChangeGiver(this);
	}
	
	/**
	 * Gets the reference of the Main Controller managing this class
	 * @return
	 */
	public MainController getMainController() {
		return mCtrl;
	}
	
	/**
	 * Gets the coin receiver initialized by the transaction controller
	 * @return Coin Receiver
	 */
	public CoinReceiver getCoinReceiver() {
		return coinReceiver;
	}
	
	/**
	 * Gets the dispense controller initialized by the transaction controller
	 * @return Dispense Controller
	 */
	public DispenseController getDispenseCtrl() {
		return dispenseCtrl;
	}
	
	/**
	 * Gets the change giver controller initialized by the transaction controller
	 * @return
	 */
	public ChangeGiver getChangeGiver() {
		return changeGiver;
	}
	
	/**
	 * Displays the Customer Panel Dialog
	 */
	public void displayCustomerPanel() {
		if(cPanel == null) cPanel = new CustomerPanel(mCtrl.getSimulatorControlPanel(), this);
		cPanel.display();
	}
	
	/**
	 * Refreshes the Customer Panel. Used when Maintainer logs out
	 */
	public void refreshCustomerPanel() {
		
	}
	
	/**
	 * This operation starts a customer transaction
	 * @param drinkIdentifier Identifier of the drink brand selected
	 */
	public void startTransaction(int drinkIdentifier) {
		try {
			//Sets the session variables
			selection = drinkIdentifier;
			price = this.mCtrl.getStoreController().getStore(StoreType.DRINKS).getStoreItem(selection).getContent().getValue();
			
			//Instruct controllers to do their work
			changeGiver.resetChange();
			dispenseCtrl.resetCan();
			changeGiver.displayChangeStatus();
			dispenseCtrl.allowSelection(false);
			coinReceiver.startReceive();
			GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.AllowTerminate, true);
		} 
		catch (VMCSException e) {
			new MessageDialog(cPanel, "Retrieval of Drink Item Failed!");
		}
	}
	
	/**
	 * This operation processes the money received by the coin receiver
	 * @param total The current total money inserted at the coin receiver
	 */
	public boolean processMoneyReceived(int total) {
		return total >= price;
	}
	
	/**
	 * This operation is performed when the coin entry is complete and 
	 * money received is sufficient to dispense a drink
	 */
	public void completeTransaction() {
		//Dispensing Drink
		if(! drinkDispensed) {
			drinkDispensed = dispenseCtrl.dispenseDrink(selection);
			
			if(! drinkDispensed) {
				new MessageDialog(cPanel, "Drink Dispensing Failed!");
			}
		}
		
		//Giving Change
		if(! changeGiven) {
			changeGiven = changeGiver.giveChange(coinReceiver.getTotalInserted() - price);
			
			if(! changeGiven) {
				new MessageDialog(cPanel, "Change Giving Failed!");
			}
		}
		
		//Storing Cash
		if(! coinReceiver.storeCash()) 
			new MessageDialog(cPanel, "Store Coin Has Failed!");
		
		//Resetting UI
		changeGiver.displayChangeStatus();
		dispenseCtrl.allowSelection(true);
		this.resetTransactionVariables();
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.AllowTerminate, false);
	}
	
	/**
	 * Terminates a transaction due to a fault, causing the coin 
	 * receiver to refund the money inserted to the customer 
	 */
	public void terminateFault() {
		changeGiven = changeGiver.giveChange(coinReceiver.getTotalInserted());
		coinReceiver.refundCash();
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.AllowCoinReceive, false);
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.SetChange,false);
		dispenseCtrl.allowSelection(true);
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.ResetCoinsInserted);
		//GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.AllowTerminate, false);
	}
	
	/**
	 * Terminates a transaction. The drink selection box will be deactivated and money refunded.
	 * The drink selection box will then be reactivated.
	 */
	public void terminateTransaction() {
		changeGiven = changeGiver.giveChange(coinReceiver.getTotalInserted());
		coinReceiver.refundCash();
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.AllowCoinReceive, false);
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.SetChange,false);
		dispenseCtrl.allowSelection(true);
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.ResetCoinsInserted);
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.AllowTerminate, false);
	}
	
	/**
	 * Resets the values needed for a transaction
	 */
	private void resetTransactionVariables() {
		changeGiven 	= false;
		drinkDispensed 	= false;
		price			= -1;
		selection		= -1;
		GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.ResetCoinsInserted);
	}
}
